﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Netfraction.Common.DataStorage;
using Community.CsharpSqlite.SQLiteClient;

namespace Netfraction.Hub.Scripting
{
	[CLSCompliant(true)]
	public class CodeSource
	{
		public CodeSource(string fileName, SourceCodeType codeType)
		{
			this.FileName = fileName;
			this.CodeType = codeType;
		}

		public CodeSource()
		{

		}

		private static Dictionary<int, Scripting.SourceCodeType> codeTypeCache = null;

		public static Dictionary<int, CodeSource> LoadCodeSources()
		{
			SqlDataStore _sqlStore = new SqlDataStore(ScriptingDataSource.Source, "codeSources.db");
			CreateDatabase(_sqlStore);

			if (codeTypeCache == null && !LoadCodeTypes(_sqlStore))
				return null;

			Dictionary<int, CodeSource> result = new Dictionary<int, CodeSource>();
			using (SqliteDataReader sdr = _sqlStore.ExecuteQuery("SELECT FileName, CodeType, SourceId FROM CodeSource ORDER BY SourceId"))
			{
				if (sdr != null)
				{
					int codeTypeId;
					while (sdr.Read())
					{
						try
						{
							codeTypeId = sdr.GetInt32(1);  // CodeType
							result.Add(sdr.GetInt32(2),
								new CodeSource(
									sdr.IsDBNull(0) ? string.Empty : sdr.GetString(0),
									codeTypeCache.ContainsKey(codeTypeId) ? codeTypeCache[codeTypeId] : codeTypeCache[0]
								));
						}
						catch (InvalidCastException ice)
						{
							Console.WriteLine(ice.Message);
						}
						catch (Exception e)
						{
							Console.WriteLine(e.Message);
						}
					}
				}
			}

			return result;
		}

		private static bool LoadCodeTypes(SqlDataStore _sqlStore)
		{
			codeTypeCache = new Dictionary<int, SourceCodeType>();
			using (SqliteDataReader sdr = _sqlStore.ExecuteQuery("SELECT Type, CodeTypeId FROM CodeType ORDER BY CodeTypeId"))
			{
				if (sdr != null)
				{
					while (sdr.Read())
					{
						try
						{
							codeTypeCache.Add(sdr.GetInt32(1), (Scripting.SourceCodeType)Enum.Parse(typeof(Scripting.SourceCodeType), sdr.GetString(0)));
						}
						catch (ArgumentException ae)
						{
							Console.WriteLine(ae.Message);  // sdr.GetString(0) is an empty string, or not a named constant in the enum
						}
						catch (InvalidCastException ice)
						{
							Console.WriteLine(ice.Message);
						}
						catch (Exception e)
						{
							Console.WriteLine(e.Message);
						}
					}
				}
			}
			return true;
		}

		/*public static CodeSource LoadFromDatabase(int sourceId)
		{
			SqlDataStore _sqlStore = new SqlDataStore(ScriptingDataSource.Source, "codeSources.db");
			CreateDatabase(_sqlStore);

			CodeSource newSource = new CodeSource();
			int codeTypeId = 0;
			using (SqliteDataReader sdr = _sqlStore.ExecuteQuery(String.Format(System.Globalization.CultureInfo.InvariantCulture, "SELECT FileName, CodeType FROM CodeSource WHERE SourceId={0} LIMIT 1", sourceId)))
			{
				if (sdr != null)
				{
					while (sdr.Read())
					{
						try
						{
							newSource.FileName = sdr.IsDBNull(0) ? string.Empty : sdr.GetString(0);
							codeTypeId = sdr.GetInt32(1);  // CodeType
						}
						catch (InvalidCastException ice)
						{
							Console.WriteLine(ice.Message);
						}
						catch (Exception e)
						{
							Console.WriteLine(e.Message);
						}
					}
				}
			}

			if (codeTypeId == 0)
				return null;

			using (SqliteDataReader sdr = _sqlStore.ExecuteQuery(String.Format(System.Globalization.CultureInfo.InvariantCulture, "SELECT Type FROM CodeType WHERE CodeTypeId={0} LIMIT 1", codeTypeId)))
			{
				if (sdr != null)
				{
					while (sdr.Read())
					{
						try
						{
							newSource.CodeType = (Scripting.SourceCodeType)Enum.Parse(typeof(Scripting.SourceCodeType), sdr.GetString(0));
							return newSource;
						}
						catch (ArgumentException ae)
						{
							Console.WriteLine(ae.Message);  // sdr.GetString(0) is an empty string, or not a named constant in the enum
						}
						catch (InvalidCastException ice)
						{
							Console.WriteLine(ice.Message);
						}
						catch (Exception e)
						{
							Console.WriteLine(e.Message);
						}
					}
				}
			}
			_sqlStore.Disconnect();

			return null;
		}*/

		public static int GetCodeSourceId(CodeSource source)
		{
			SqlDataStore _sqlStore = new SqlDataStore(ScriptingDataSource.Source, "codeSources.db");
			CreateDatabase(_sqlStore);

			using (SqliteDataReader sdr = _sqlStore.ExecuteQuery(String.Format(System.Globalization.CultureInfo.InvariantCulture, "SELECT SourceId FROM CodeSource WHERE FileName='{0}' AND CodeType={1} LIMIT 1", source.FileName, (int)source.CodeType)))
			{
				if (sdr != null)
				{
					while (sdr.Read())
					{
						try
						{
							return sdr.GetInt32(0);
						}
						catch (ArgumentException ae)
						{
							Console.WriteLine(ae.Message);  // sdr.GetString(0) is an empty string, or not a named constant in the enum
						}
						catch (InvalidCastException ice)
						{
							Console.WriteLine(ice.Message);
						}
						catch (Exception e)
						{
							Console.WriteLine(e.Message);
						}
					}
				}
			}
			_sqlStore.Disconnect();

			return 0;
		}

		private static void CreateDatabase(SqlDataStore _sqlStore)
		{
			//_sqlStore.ExecuteNonQuery("DROP TABLE CodeSource");
			//_sqlStore.ExecuteNonQuery("DROP TABLE CodeType");

			if (!_sqlStore.TableExists("CodeSource"))
			{
				_sqlStore.ExecuteNonQuery("CREATE TABLE IF NOT EXISTS CodeSource (SourceId INTEGER PRIMARY KEY AUTOINCREMENT, FileName Text, CodeType INTEGER)");
				//_sqlStore.ExecuteNonQuery("INSERT INTO CodeSource (CodeType) VALUES (1)"); // built-in types will have this CodeSource
				RegisterCodeSourceInternal(_sqlStore, string.Empty, SourceCodeType.BuiltIn); // built-in types will have this CodeSource
			}
			if (!_sqlStore.TableExists("CodeType"))
			{
				// IF NOT EXISTS is redundant. We wrap this block in the if-statement to ensure these types always exists.
				_sqlStore.ExecuteNonQuery("CREATE TABLE IF NOT EXISTS CodeType (CodeTypeId INTEGER PRIMARY KEY AUTOINCREMENT, Type TEXT)");
				string[] codeTypes = new string[] { "BuiltIn", "CompiledAssembly", "DLRScript" };
				foreach (string codeType in codeTypes)
				{
					_sqlStore.ExecuteNonQuery(String.Format(System.Globalization.CultureInfo.InvariantCulture, "INSERT INTO CodeType (Type) VALUES ('{0}')", codeType));
				}
			}
		}

		public static bool RegisterCodeSource(CodeSource codeSource)
		{
			return RegisterCodeSource(codeSource.FileName, codeSource.CodeType);
		}

		public static bool RegisterCodeSource(string fileName, SourceCodeType codeType)
		{
			SqlDataStore _sqlStore = new SqlDataStore(ScriptingDataSource.Source, "codeSources.db");
			CreateDatabase(_sqlStore);

			bool success = RegisterCodeSourceInternal(_sqlStore, fileName, codeType);

			_sqlStore.Disconnect();

			return success;
		}

		[SqlVulnerabilityAttribute("Potential SQL Injection flaw.")]
		private static bool RegisterCodeSourceInternal(SqlDataStore _sqlStore, string fileName, SourceCodeType codeType)
		{
			return _sqlStore.ExecuteNonQuery(String.Format(System.Globalization.CultureInfo.InvariantCulture,
				"INSERT INTO CodeSource (FileName, CodeType) VALUES ('{0}', {1})",
				fileName,
				(int)codeType)) == 1;

		}

		private static void EnumerateCodeSourcesToConsole(SqlDataStore _sqlStore)
		{
			SqliteDataReader sdr = _sqlStore.ExecuteQuery("SELECT SourceId, FileName, CodeType.Type FROM CodeSource JOIN CodeType ON CodeType.CodeTypeId=CodeSource.CodeType");
			if (sdr != null)
			{
				Console.WriteLine("CodeSources:");
				while (sdr.Read())
				{
					Console.WriteLine("{0}: <{1}> <{2}>",
						sdr.GetInt32(0),    // SourceId
						(sdr.IsDBNull(1) ? string.Empty : sdr.GetString(1)), // FileName
						sdr.GetString(2));  // CodeType
				}
			}
		}

		private static void EnumerateCodeTypesToConsole(SqlDataStore _sqlStore)
		{
			SqliteDataReader sdr = _sqlStore.ExecuteQuery("SELECT CodeTypeId, Type FROM CodeType");
			if (sdr != null)
			{
				Console.WriteLine("CodeTypes:");
				while (sdr.Read())
				{
					try
					{
						Console.WriteLine("{0}: <{1}>",
							sdr.GetInt32(0),    // CodeTypeId
							sdr.GetString(1));  // Type
					}
					catch (InvalidCastException ice)
					{
						Console.WriteLine(ice.Message);
					}
					catch (Exception e)
					{
						Console.WriteLine(e.Message);
					}
				}
			}
		}

		public string FileName { get; set; }
		public SourceCodeType CodeType { get; set; }
	}
}
